/*
 *	Copyright (C) 2008 - Mauricio Bieze Stefani
 *	This file is part of the Insano Game Engine.
 *
 *	Insano is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	Insano is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with Insano.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "GameClient.h"
#include "Protocolo.h"
#include "Parser.h"
#include <string>
#include "GameUI.h"
#include "MBSTrace.h"
#include "Animation.h"
#include <time.h>

using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

GameClient::GameClient()
{
	protocolState=DISCONNECTED;
}

GameClient::~GameClient()
{
//	if(device)
//		device->drop();
	config.save("client.xml");
}

GameClient::GameState GameClient::changeGameState(GameState newstate)
{
	// hide all windows
	gui.setVisible(ID_CHAT_WINDOW,false);
	gui.setVisible(ID_PROGRESS_WINDOW,false);
	gui.setVisible(ID_LOGIN_WINDOW,false);
	gui.setVisible(ID_CHAR_WINDOW,false);
	gui.setVisible(ID_WELCOME_WINDOW,false);

	if(newstate == PLAYING)
	{
		if( setMap(std::string("world")) )
		{
			newstate = FATAL_ERROR;
			TRACE_ERROR("GAME STATE IS FATAL_ERROR");
		}
		else
			gui.setVisible(ID_CHAT_WINDOW,true);
	}
	else
	{
		// all states except PLAYING use the login map as scenary
		if(setMap(std::string("login")))
		{
			newstate = FATAL_ERROR;
			TRACE_ERROR("GAME STATE IS FATAL_ERROR");
		}

		if(newstate == LOGIN_SCREEN)
			gui.moveToScreenCenter(ID_LOGIN_WINDOW);
		else if(newstate == CHAR_SCREEN)
			gui.moveToScreenCenter(ID_CHAR_WINDOW);
		else if(newstate == WELCOME_SCREEN)
			gui.moveToScreenCenter(ID_WELCOME_WINDOW);
		else if(newstate == PROGRESS_SCREEN)
			gui.moveToScreenCenter(ID_PROGRESS_WINDOW);

		if(newstate == FATAL_ERROR)
			TRACE_ERROR("GAME STATE IS FATAL_ERROR");
	}
	return gameState=newstate;
}

int GameClient::setMap(const std::string& mapname)
{
	if(currentMap==mapname)
		return 0;

	currentMap=mapname;

	smgr->clear();

	world = mapManager.loadMap(smgr, mapname);

	if(world==NULL)
	{
		TRACE_ERROR("loading map %s", mapname.c_str());
		return -1;
	}

	if(mapname=="login")
	{
		me.camera = new Camera(smgr);
		me.camera->node->setPosition(vector3df(0,0,-30));
	}

	if(mapname=="world")
	{

		//aabbox3df bbox=world->getBoundingBox();
		//TRACE_INFO("MaxEdge=(%f,%f,%f)",bbox.MaxEdge.X,bbox.MaxEdge.Y,bbox.MaxEdge.Z);
		//TRACE_INFO("MinEdge=(%f,%f,%f)",bbox.MinEdge.X,bbox.MinEdge.Y,bbox.MinEdge.Z);

		PlayerChar *pc = eFac.CreatePlayerChar("sydney");
		if(pc==NULL)
		{
			TRACE_ERROR("error loading self char");
			return -1;
		}
		// copy contents to static member
		me=*pc;

		// delete the PlayerChar but keep the node
		pc->setNode(NULL);
		delete pc;

		// if available, use any camera defined in the map as start position
		if(ISceneNode *cameraNode = world->getCamera())
		{
			me.setPosition(cameraNode->getPosition());
			TRACE_INFO("Using map defined camera");
		}

		vector3df radius = (me.getBoundingBox().MaxEdge - me.getBoundingBox().getCenter());
		ISceneNodeAnimator* aaa = smgr->createCollisionResponseAnimator( world->selector, me.getNode(), radius,
								   vector3df(0,-100,0), me.getCollisionTranslate());
		me.getNode()->addAnimator(aaa);
		aaa->drop();

		me.camera = new Camera(smgr);
		me.camera->setWorld(world->selector);
		me.camera->setParent(me.getNode());
		me.camera->setTranslate(me.getCameraTranslate());
	}

	return 0;
}

int GameClient::conectar()
{
	login_timeout=10000;	//ms
	int rc=slot.conectar(config.host,config.port);
	if(0==rc)
		sockSel.set(slot.getFD());
	else
	{
		TRACE_ERROR("slot.conectar = %d", rc);
		desconectar();
	}
	return rc;
}

int GameClient::desconectar()
{
	slot.desconectar();
	changeGameState(LOGIN_SCREEN);
	protocolState=DISCONNECTED;
	return 0;
}

int GameClient::handleNet()
{
	MBSSlot::EstadoSlot stat=slot.pegaEstado();

	if( (stat==MBSSlot::LIVRE) || (!slot.validFD()) )
		return 0;	// not connected

	int rc=sockSel.Select();
	if(rc<0)
	{
		TRACE_ERROR("Select() = %d", rc);
		desconectar();
		return -1;
	}

	if(sockSel.isException())
	{
		TRACE_ERROR("socket exception");
		desconectar();
		return -1;
	}

	switch(stat)
	{
		case MBSSlot::RESERVADO:
		{
			if(sockSel.isWrite())
			{
				slot.setaEstado(MBSSlot::CONECTADO);
				if(protocolState==WAIT_LOGIN)
				{
					if(sendLogin()==0)
						changeGameState(PROGRESS_SCREEN);
				}
				else if(protocolState==WAIT_NEW_ACC)
				{
					if(sendNewAccount()==0)
						changeGameState(PROGRESS_SCREEN);
				}
			}
		}
		break;

		case MBSSlot::CONECTADO:
		{
			if(sockSel.isRead())
			{
				if(slot.receiveLoop()<=0)
					desconectar();
			}
			MBSBuffer *pacote;
			while((pacote=slot.receive())!=NULL)
			{
//				cout << "pacote != NULL" << endl;
				unsigned char comando;
				pacote->readByte(&comando);
				switch((COMANDO)comando)
				{
					// ===========
					case LOGIN_OK:
					// ===========
					{
						TRACE_INFO("Login acepted");
						sendGetCharList();
						protocolState=WAIT_CHAR_LIST;
						changeGameState(CHAR_SCREEN);
					}
					break;
					// ================
					case CREATE_ACC_OK:
					// ================
					{
						TRACE_INFO("New account created");
						sendLogin();
					}
					break;
					// ===============
					case CHAR_LST_REP:
					// ===============
					{
						pacote->readByte(&auxCharList.numChars);

						IGUIComboBox *icb=static_cast<IGUIComboBox*>(gui.getElement(ID_CHAR_COMBO));
						if(icb==NULL)
						{
							TRACE_ERROR("Could not find ID_CHAR_COMBO(%d) declaration", ID_CHAR_COMBO);
							break;
						}
						icb->clear();
						for(unsigned char x=0; x<auxCharList.numChars; x++)
						{
							pacote->readLong(&auxCharList.chars[x].id);
							pacote->readString(auxCharList.chars[x].name, MAX_NAME);
							pacote->readByte(&auxCharList.chars[x].race);
							pacote->readByte(&auxCharList.chars[x].gender);

							{
								wchar_t temp[MAX_TEXT_SIZE+1];
								mbstowcs(temp, auxCharList.chars[x].name, MAX_NAME);
								icb->addItem(temp);
							}
						}
						icb->addItem(L"Create new...");
						icb->setSelected(0);
						gui.setVisible(ID_CHAR_NEW, (auxCharList.numChars==0));
					}
					break;
					// ================
					case LOGIN_CHAR_OK:
					// ================
						changeGameState(PLAYING);
					break;
					// ==================
					case LOGIN_CHAR_DENY:
					// ==================
						desconectar();
					break;
					// ===================
					case CREATE_CHAR_OK:
					case CREATE_CHAR_DENY:
					// ===================
						sendGetCharList();
						protocolState=WAIT_CHAR_LIST;
					break;
					// ===============
					case CHAT_SRV_CLI:
					// ===============
					{
						char remetente[MAX_NAME+1];
						pacote->readString(remetente,MAX_NAME);

						char mensagem[MAX_MSG+1];
						pacote->readString(mensagem,MAX_MSG);

						TRACE_INFO("[CHAT] %s: %s", remetente, mensagem);

						std::string mesg(remetente);
						mesg += std::string(": ") + std::string(mensagem);
						gui.appendText(ID_CHAT_LOG, mesg.c_str());
					}
					break;
					default:
						TRACE_WARN("Comando = %d invalido para estado = %d", (int)comando, slot.pegaEstado());
					break;
				}
				delete pacote;
			}
		}
		break;
		case MBSSlot::LIVRE:
		case MBSSlot::LOGIN:
		case MBSSlot::HANDSHAKE:
			TRACE_WARN("unexpected slot state %d in handleNet()", stat);
		break;
	}
	return 0;
}

int GameClient::sendLogin()
{
	int rc=0;
	MBSBuffer *b=new MBSBuffer(sizeof(COMANDO)+strlen(config.username)+1+strlen(config.password)+1);
	b->writeByte((COMANDO)LOGIN_REQ);
	b->writeString(config.username, MAX_NAME);
	b->writeString(config.password, MAX_NAME);
	if( (rc=slot.enviar(b)) )
	{
		TRACE_ERROR("sending login and password = %d", rc);
		desconectar();
		rc=-1;
	}
	delete b;
	return rc;
}

int GameClient::sendNewAccount()
{
	int rc=0;
	MBSBuffer *b=new MBSBuffer(sizeof(COMANDO)+strlen(config.username)+1+strlen(config.password)+1);
	b->writeByte((COMANDO)CREATE_ACC_REQ);
	b->writeString(config.username, MAX_NAME);
	b->writeString(config.password, MAX_NAME);
	if( (rc=slot.enviar(b)) )
	{
		TRACE_ERROR("trying to create new account = %d", rc);
		desconectar();
		rc=-1;
	}
	delete b;
	return rc;
}

int GameClient::sendGetCharList()
{
	int rc=0;
	MBSBuffer *b=new MBSBuffer(sizeof(COMANDO));
	b->writeByte((COMANDO)CHAR_LST_REQ);
	if( (rc=slot.enviar(b)) )
	{
		TRACE_ERROR("trying to retrieve char list", rc);
		desconectar();
		rc=-1;
	}
	delete b;
	return rc;
}

int GameClient::sendChat(const std::string& dest, const std::string& msg)
{
	int rc=0;
	MBSBuffer *p=new MBSBuffer(sizeof(COMANDO)+(strlen(dest.c_str())+1)+(strlen(msg.c_str())+1));	//comando + dest + 0 + msg + 0
	p->writeByte((COMANDO)CHAT_CLI_SRV);
	p->writeString(dest.c_str(), MAX_NAME);
	p->writeString(msg.c_str(), MAX_MSG);
	if(slot.enviar(p))
	{
		TRACE_ERROR("while trying to send the packet. Perhaps you are not connected...");
		desconectar();
		rc=-1;
	}
	delete p;
	return rc;
}

int GameClient::sendLoginChar(unsigned long id)
{
	int rc=0;
	MBSBuffer *b=new MBSBuffer(sizeof(COMANDO)+sizeof(id));
	b->writeByte((COMANDO)LOGIN_CHAR_REQ);
	b->writeLong(id);
	if( (rc=slot.enviar(b)) )
	{
		TRACE_ERROR("trying to send login char command = %d", rc);
		desconectar();
		rc=-1;
	}
	delete b;
	return rc;
}

int GameClient::sendCreateChar(const char *newChar, unsigned char race, unsigned char gender)
{
	int rc=0;
	MBSBuffer *b=new MBSBuffer(sizeof(COMANDO)+MAX_NAME+1+1+1);
	b->writeByte((COMANDO)CREATE_CHAR_REQ);
	b->writeString(newChar, MAX_NAME);
	b->writeByte(race);
	b->writeByte(gender);
	if( (rc=slot.enviar(b)) )
	{
		TRACE_ERROR("trying to create new char = %d", rc);
		desconectar();
		rc=-1;
	}
	delete b;
	return rc;
}

/*
	Game startup and main loop
*/
int GameClient::run()
{
    config.load("client.xml");

	device = createDevice( 	config.video.driver,
							dimension2d<u32>(config.video.horizontal,config.video.vertical),
							config.video.bpp,
							config.video.fullscreen,
							false,	//stencilbuffer (shadows)
							false,	//vsync
							this);
	if(device==NULL)
	{
	    TRACE_ERROR("NULL device");
		return -1;
	}

	device->setResizable(true);

	driver = device->getVideoDriver();
	smgr = device->getSceneManager();
	smgr->getParameters()->setAttribute(irr::scene::ALLOW_ZWRITE_ON_TRANSPARENT, false);

	gui.env = device->getGUIEnvironment();
	gui.updateScreenSize();

	mapManager.setIrrlichtDevice(device);
	if(mapManager.initMapLoader("maps.xml"))
	{
        TRACE_ERROR("loading maps.xml definition file");
		return -1;
	}

	eFac.setIrrlichtDevice(device);
	if(eFac.initCreatureLoader("creatures.xml"))
	{
        TRACE_ERROR("loading creatures.xml definition file");
		return -1;
	}

	// pre-load windows

	gui.env->loadGUI("gui.xml");

	// autofill login stuff from client.xml

	gui.setText(ID_LOGIN_HOST, config.host);
	gui.setText(ID_LOGIN_PORT, L"%d", config.port);
	gui.setText(ID_LOGIN_USER, config.username);

	gui.setVisible(ID_CHAT_EDIT, false);

	//font

	{
		IGUISkin* skin = gui.env->getSkin();
		IGUIFont* font = gui.env->getFont("media/lucida.xml");
		if (font) skin->setFont(font);
	}

	// debug info box

	info=gui.env->addStaticText(L"",
				   rect<s32>(config.video.horizontal-150,10,config.video.horizontal-10,100),
				   true, // border?,
				   true, 0, -1, true
				   );

	changeGameState(LOGIN_SCREEN);

	// loop principal

	const float velocidade=0.03;		//"unidades" por milisegundo
	const float velocidadeAngular=0.15;

	tempo.reset();

	ISceneNode *selectedNode=NULL;

	while (device->run())
	{
		unsigned long lapse=tempo.loop();

		handleNet();

		switch(gameState)
		{
			case PROGRESS_SCREEN:
			{
				login_timeout-=lapse;
				gui.setText(ID_PROGRESS_TEXT,"Connecting, timeout in %d seconds",(login_timeout/1000));
				if(login_timeout<0)
					desconectar();
			}
			case WELCOME_SCREEN:
			case LOGIN_SCREEN:
			case CHAR_SCREEN:
			{
				// just show debug info while waiting for user input

				wchar_t wcinfo[20];
				swprintf(wcinfo,20,L"FPS=%d",driver->getFPS());
				info->setText(wcinfo);
			}
			break;

			case PLAYING:
			{
				ISceneNode *n=smgr->getSceneCollisionManager()->getSceneNodeFromScreenCoordinatesBB(mouse.pos);
				if((n) && (n->getType()!=ESNT_TERRAIN))
				{
					if(n!=selectedNode)
					{
						if(selectedNode)
							selectedNode->setDebugDataVisible( selectedNode->isDebugDataVisible() & !EDS_BBOX );
						selectedNode=n;
						n->setDebugDataVisible( n->isDebugDataVisible() | EDS_BBOX );
					}
				}
				else
				{
					if(selectedNode)
					{
						selectedNode->setDebugDataVisible( selectedNode->isDebugDataVisible() & !EDS_BBOX );
						selectedNode=NULL;
					}
				}

				float deslocamento=lapse*velocidade;
				float deslocamentoAngular=lapse*velocidadeAngular;

				// turn left/right

				core::vector3df d = me.getRotation();	//em DEGs
				if (kbd.status[KEY_KEY_A])
					d.Y -= deslocamentoAngular;
				if (kbd.status[KEY_KEY_D])
					d.Y += deslocamentoAngular;
				me.setRotation(d);
				float diry = d.Y * DEGTORAD;				//pre-converte pra RAD

				// move forward/back

				core::vector3df c = me.getPosition();
//*
				if (kbd.status[KEY_KEY_W])
				{
					c.X += deslocamento * cos(diry);
					c.Z -= deslocamento * sin(diry);
				}
				if (kbd.status[KEY_KEY_S])
				{
					c.X -= deslocamento * cos(diry);
					c.Z += deslocamento * sin(diry);
				}

				// check terrain bounds

				if(ISceneNode *terrain = smgr->getSceneNodeFromType(irr::scene::ESNT_TERRAIN))
				{
					const aabbox3df& bbox=terrain->getBoundingBox();

					if(c.X > bbox.MaxEdge.X * 0.98)
						c.X = bbox.MaxEdge.X * 0.98;
					if(c.X < bbox.MinEdge.X * 0.98)
						c.X = bbox.MinEdge.X * 0.98;

					if(c.Z > bbox.MaxEdge.Z * 0.98)
						c.Z = bbox.MaxEdge.Z * 0.98;
					if(c.Z < bbox.MinEdge.Z * 0.98)
						c.Z = bbox.MinEdge.Z * 0.98;

					if(c.Y < bbox.MinEdge.Y)
						c.Y = bbox.MaxEdge.Y;
				}
//*/
/*
				if (kbd.status[KEY_KEY_W])
				{
					c.X = deslocamento * cos(diry);
					c.Z = -deslocamento * sin(diry);
				}
				if (kbd.status[KEY_KEY_S])
				{
					c.X = -deslocamento * cos(diry);
					c.Z = deslocamento * sin(diry);
				}


				vector3df radius = (me.getBoundingBox().MaxEdge - me.getBoundingBox().getCenter());
				core::triangle3df triout;
				bool outFalling;

				vector3df newPos = smgr->getSceneCollisionManager()->getCollisionResultPosition(world->selector,
																								me.getPosition(),
																								radius,
																								c,
																								triout,
																								outFalling,
																								0.0005f,
																								vector3df(0,-1,0));
				c = newPos;
*/
				me.setPosition(c);

				// pitch camera

				me.camera->sumPitch(mouse.getDragY()/3);
				if (kbd.status[KEY_UP])
					me.camera->sumPitch(deslocamentoAngular);
				else if (kbd.status[KEY_DOWN])
					me.camera->sumPitch(-deslocamentoAngular);

				// yaw camera

				me.camera->sumYaw(mouse.getDragX()/3);
				if (kbd.status[KEY_LEFT])
					me.camera->sumYaw(deslocamentoAngular);
				if (kbd.status[KEY_RIGHT])
					me.camera->sumYaw(-deslocamentoAngular);

				// camera distance

				me.camera->sumDistance(-10*mouse.getWheel());

				// reset camera on wheel press

				if(mouse.mid)
					me.camera->reset();

				// show debug info window

				wchar_t wcinfo[200];
				swprintf(wcinfo,200,L"FPS=%d\nPOS(%.2f,%.2f,%.2f)\nMouse pos(%d,%d)",
						driver->getFPS(),c.X,c.Y,c.Z,mouse.pos.X,mouse.pos.Y);
				info->setText(wcinfo);

				// main player animation

				if (kbd.status[KEY_KEY_W]||kbd.status[KEY_KEY_S])
					me.setAction(Animation::RUN);
				else
					me.setAction(Animation::STAND);

				//take screenshot

				if(kbd.wasHit(KEY_F12))
				{
					IImage* image = driver->createScreenShot();

					if(image)
					{
						c8 filename[64];
						time_t timet=time(NULL);

						strftime(filename, 64, "screenshot_%Y%m%d%H%M%S.jpg", localtime(&timet));

						driver->writeImageToFile(image, filename);
						image->drop();
					}
					else
						TRACE_ERROR("creating screenshot");
				}

				//create clone

				if(kbd.wasHit(KEY_KEY_C))
					me.Clone();

			} // case playing
			break;
			case FATAL_ERROR:
				TRACE_ERROR("FATAL ERROR");
				return -1;
			break;
		} //switch gameState

		if (!device->isWindowActive())  // Dont waste CPU if minimized
			device->sleep(200);

		driver->beginScene(true, true, video::SColor(0,0,0,0));
			smgr->drawAll();
			gui.env->drawAll();
		driver->endScene();

		//only update camera after collision detection

		if(gameState == PLAYING)
			me.camera->updatePosition();

		// quit game

		if(kbd.wasHit(KEY_ESCAPE))
			device->closeDevice();

	} //while device->run()
	return 0;
}

/*
	Called by Irrlicht Engine to handle events
	On return:
		- true means that event was handled here
		- false asks Irrlicht to handle it by himself
*/
bool GameClient::OnEvent(const SEvent& event)
{
	// ==================================
	if (event.EventType == EET_GUI_EVENT)
	// ==================================
	{
		switch(event.GUIEvent.Caller->getID())
		{
			case ID_WELCOME_WINDOW:
			case ID_WELCOME_WINDOW_OK:
				if 	((event.GUIEvent.EventType==EGET_BUTTON_CLICKED)
					|| (event.GUIEvent.EventType==EGET_ELEMENT_CLOSED))
				{
					//TODO: remember 'dont show' checkbox
					changeGameState(LOGIN_SCREEN);
					return true;
				}
			break;

			case ID_PROGRESS_WINDOW:
				if (event.GUIEvent.EventType==EGET_ELEMENT_CLOSED)
				{
					desconectar();
					return true;
				}
			break;

			case ID_LOGIN_WINDOW:
				if (event.GUIEvent.EventType==EGET_ELEMENT_CLOSED)
				{
					TRACE_WARN("ID_LOGIN_WINDOW -> EGET_ELEMENT_CLOSED");
					changeGameState(PLAYING);
					return true;
				}
			break;
			case ID_LOGIN_BUTTON:
				if (event.GUIEvent.EventType==EGET_BUTTON_CLICKED)
				{
					strcpy(config.username,gui.getText(ID_LOGIN_USER));
					strcpy(config.password,gui.getText(ID_LOGIN_PASS));
					strcpy(config.host,gui.getText(ID_LOGIN_HOST));
					config.port=atoi(gui.getText(ID_LOGIN_PORT));
					conectar();
					protocolState=WAIT_LOGIN;
					changeGameState(PROGRESS_SCREEN);
					return true;
				}
			break;
			case ID_LOGIN_CREATE_ACC:
				if (event.GUIEvent.EventType==EGET_BUTTON_CLICKED)
				{
					strcpy(config.username,gui.getText(ID_LOGIN_USER));
					strcpy(config.password,gui.getText(ID_LOGIN_PASS));
					strcpy(config.host,gui.getText(ID_LOGIN_HOST));
					config.port=atoi(gui.getText(ID_LOGIN_PORT));
					conectar();
					protocolState=WAIT_NEW_ACC;
					changeGameState(PROGRESS_SCREEN);
					return true;
				}
			break;

			case ID_CHAT_WINDOW:
				if (event.GUIEvent.EventType==EGET_ELEMENT_CLOSED)
					return true;	//cant close chat window
			break;

			case ID_CHAR_BUTTON:
				if (event.GUIEvent.EventType==EGET_BUTTON_CLICKED)
				{
					IGUIComboBox *icb=static_cast<IGUIComboBox*>(gui.getElement(ID_CHAR_COMBO));
					int sel=icb->getSelected();
					if(sel == auxCharList.numChars)
					{	//create new
						if(sendCreateChar(gui.getText(ID_CHAR_NEW),1,1))
						{
							protocolState=WAIT_CHAR_NEW;
							changeGameState(PROGRESS_SCREEN);
						}
					}
					else
					{
						if(sendLoginChar(auxCharList.chars[sel].id))
						{
							protocolState=WAIT_CHAR_LOGIN;
							changeGameState(PROGRESS_SCREEN);
						}
					}
					return true;
				}
			break;
			case ID_CHAR_WINDOW:
				if (event.GUIEvent.EventType==EGET_ELEMENT_CLOSED)
				{
					desconectar();
					return true;
				}
			break;
			case ID_CHAR_COMBO:
				if (event.GUIEvent.EventType==EGET_COMBO_BOX_CHANGED)
				{
					IGUIComboBox *icb=static_cast<IGUIComboBox*>(gui.getElement(ID_CHAR_COMBO));
					if(icb->getSelected() == auxCharList.numChars)
						gui.setVisible(ID_CHAR_NEW, true);
					else
						gui.setVisible(ID_CHAR_NEW, false);
					return true;
				}
			break;

			default:
			return false;	//let irrlicht decide what to do
		}
	}

	// ==================================================
	else if (event.EventType == irr::EET_KEY_INPUT_EVENT)
	// ==================================================
	{
		if(gameState==PLAYING)
		{
			if((event.KeyInput.Key==KEY_RETURN) && event.KeyInput.PressedDown)
			{	//enter inverte o dono do teclado GUI <-> jogo
				if(kbd.kbdgui)
				{
					if(protocolState!=DISCONNECTED)
					{
						// send message and hide edit
						std::string mesg(gui.getText(ID_CHAT_EDIT));
						if(mesg!="")	//send only if player typed something
							sendChat("",mesg);
					}
					gui.setVisible(ID_CHAT_EDIT, false);
					gui.setText(ID_CHAT_EDIT, L"");
				}
				else	// show chat edit box
				{
					gui.setVisible(ID_CHAT_EDIT, true);
					gui.setFocus(ID_CHAT_EDIT);
				}
				kbd.kbdgui=!kbd.kbdgui;
				kbd.clear();
				return true;
			}
			else
			{
				if(kbd.kbdgui)		//if it belongs to the gui
					return false;	//let irrlicht handle it
				else				//belongs to the game...
					return kbd.handleIrrlicht(event);
			}
		}
	}

	// ==============================================
	else if(event.EventType == EET_MOUSE_INPUT_EVENT)
	// ==============================================
	{
		if(gameState==PLAYING)
		{
			if((kbd.kbdgui)||(kbd.status[KEY_KEY_M]))
				return false;

			return mouse.handleIrrlicht(event);
		}
	}

    return false;
}
